
#include "arch_datos.h"

ArchivoDatos::ArchivoDatos(std::string& nombre, unsigned int tam_bloque,unsigned int tam_bloque_indice) : Archivo(nombre, tam_bloque, tam_bloque_indice){	
	indice_primario = NULL;
	indice_secundario = NULL;
}

IndiceSec * ArchivoDatos::get_indice(){
	return indice_secundario;	
}

void ArchivoDatos::set_indice(IndiceSec * ind){
	if( this->indice_secundario != NULL)
		delete indice_secundario;
	indice_secundario = ind;
}

unsigned int ArchivoDatos::get_esp_libre_ind_sec()  throw(ExcepArchCerrado, ExcepLeerBloque){
	if (this->indice_secundario != NULL)
		return this->indice_secundario->get_espacio_libre();
	else
		return 0;	
}
 		 										
unsigned int ArchivoDatos::get_cant_regs_ind_sec() throw(ExcepArchCerrado){
	if (this->indice_secundario != NULL)
		return this->indice_secundario->get_cant_regs();
	else
		return 0;	
}

long ArchivoDatos::get_tam_ind_sec() throw(ExcepArchCerrado) {
	if (this->indice_secundario != NULL)
		return this->indice_secundario->get_tam();
	else
		return 0;		
}

void ArchivoDatos::consultar_secuencial(std::vector<ParesDatos*> &consultas, std::vector<TipoStruct*> &resultado)
	
											 throw(ExcepLeerBloque){
	unsigned int cont = 1;
	Bloque* bloque_actual = NULL;
	TipoStruct* reg_base = this->get_registro_base();
	std::vector<TipoStruct*> res_temp;
	(Debug::get_instance() )->mostrar_mensaje("< CONSULTA SECUENCIAL> Se consulta en el archivo "+ this->get_nombre());
	while (cont < get_cant_bloques()){ //recorro todos los bloques
		try{
			bloque_actual = Buffer::get_instance()->get_bloque(*this,cont);
			//Hago la consulta sobre todos los regs del bloque
			bloque_actual->consultar_desde(get_primera_pos(), reg_base, consultas, res_temp);
			this->concatenar(resultado, res_temp);
		}catch (ExcepLeerBloque &e){
			if (bloque_actual != NULL) { 
				delete (bloque_actual); 
				bloque_actual = NULL;
			}
			throw e;	//la sigo propagando por ahora
		}
		if (bloque_actual != NULL) { 
			delete (bloque_actual); 
			bloque_actual = NULL;
		}
		cont++;
	}
}

bool ArchivoDatos::consultar_indice_secundario(std::vector<ParesDatos*> &consultas, std::vector<TipoStruct*> &resultado)
												 throw(ExcepLeerBloque){
	if(indice_secundario == NULL) 
		return false;
	//el indice secundario busca los reg que cumplan con su campo clave y los devuelve en resultado
	std::vector<TipoStruct*> res_claves;
	bool campo_clave_ok = indice_secundario->consultar(consultas,res_claves); 
	if (!campo_clave_ok) //no busco por el campo clave del indice sec
		return false;
	//coincide con el campo clave del indice sec, busco contra el indice
	//hay mas de una restriccion y tenemos todos los que cumplen con el campo clave del ind sec
	//ademas verifico que el haya resultados de la busqueda en el indice sec.
	if (res_claves.size()!=0){ 
		std::vector<ParesDatos*> temp_cons;
		std::vector<TipoStruct*> temp_res;
		unsigned int cont=0;	
		ParesDatos* par;
		TipoDato* clave = NULL;
		while(cont<res_claves.size()){
			clave = (*res_claves[cont])[0].clonar();
			par = new ParesDatos(0, clave);
			temp_cons.push_back(par); //le cargo un consulta por campo clave, para hacer un acceso directo
			consultar(temp_cons,temp_res); //me devuelve en temp_res el registro buscado por la clave				
			delete par;
			temp_cons.clear();
			concatenar(resultado,temp_res);
			delete(res_claves[cont]);
			cont++;
		}
		if(consultas.size() != 1)
			this->seleccionar_validos(consultas,resultado);
	}
	return true;
}

bool ArchivoDatos::quitar_indice_secundario(std::vector<ParesDatos*> &elementos, std::vector<TipoStruct*> &resultado)
												 throw(ExcepLeerBloque){
	if(indice_secundario == NULL) 
		return false;
	//hago primero una busqueda para saber si hya registros que quitar
	std::vector<TipoStruct*> res_claves;
	bool campo_clave_ok = indice_secundario->consultar(elementos,res_claves); 
	if (!campo_clave_ok) //no busco por el campo clave del indice sec
		return false;
	//coincide con el campo clave del indice sec, busco contra el indice
	//hay mas de una restriccion y tenemos todos los que cumplen con el campo clave del ind sec
	//ademas verifico que el haya resultados de la busqueda en el indice sec.
	if (res_claves.size()!=0){ 
		std::vector<ParesDatos*> temp_elem;
		std::vector<TipoStruct*> temp_res;
		unsigned int cont=0;	
		ParesDatos* par;
		TipoDato* clave = NULL;
		while(cont<res_claves.size()){
			clave = (*res_claves[cont])[0].clonar();
			par = new ParesDatos(0, clave);
			temp_elem.push_back(par); //le cargo un consulta por campo clave
			temp_res.clear();
			consultar(temp_elem,temp_res); //me devuelve en temp_res el registro buscado por la clave				
			if(temp_res.size() != 0){
				if (cumple_condiciones(*(temp_res[0]),elementos)){
					delete (temp_res[0]);
					temp_res.clear();
					this->quitar(temp_elem,temp_res); 
					//me devuelve en temp_res el resgistro quitado del indexado ppal
					concatenar(resultado,temp_res);
				}
				else
					delete (temp_res[0]);
							
			}
			temp_res.clear();
			delete par;
			delete(res_claves[cont]);
			temp_elem.clear();
			cont++;
		}
	}			
	return true;
}

void ArchivoDatos::quitar_en_indice_primario(TipoStruct * reg){
	if (indice_primario != NULL)
		indice_primario->quitar(&(*reg)[0]); //quito la clave del indice primario
}

void ArchivoDatos::quitar_secuencial(std::vector<ParesDatos*> &elementos, std::vector<TipoStruct*> &resultado)
											throw (ExcepLeerBloque){
	resultado.clear();
	unsigned int cont = 1;
	Bloque* bloque_actual = NULL;
	TipoStruct* reg_base = get_registro_base();
	std::vector<TipoStruct*> res_temp;
	(Debug::get_instance() )->mostrar_mensaje("< QUITAR SECUENCIAL> Se quita en el archivo "+ this->get_nombre());
	unsigned int pos_res=0;
	unsigned int size_res=0;
	unsigned int pos_act=0;

	while (cont < cant_bloques){ //recorro todos los bloques
		try{
			bloque_actual = Buffer::get_instance()->get_bloque(*this,cont);
			pos_act = get_primera_pos();
			TipoStruct* reg = (TipoStruct*)reg_base->clonar();
			bool encontrado = bloque_actual->buscar_primero_desde(pos_act, reg, elementos, pos_res, size_res);
			if (!encontrado) delete reg;
			while (encontrado) {
				res_temp.push_back(reg);
				sacar_reg_bloque(bloque_actual, reg ,pos_res,size_res); //lo saco si es que lo encontre
				
				quitar_en_indice_primario(reg);
				
				if(indice_secundario != NULL)
					indice_secundario->quitar(reg);	
				pos_act = pos_res;
				reg = (TipoStruct*)reg_base->clonar();
				encontrado = bloque_actual->buscar_primero_desde(pos_act, reg, elementos, pos_res, size_res);
				if (!encontrado) delete reg;
			}
			this->concatenar(resultado, res_temp);
		}catch (ExcepLeerBloque &e){
			if (bloque_actual != NULL) { 
				delete (bloque_actual); 
				bloque_actual = NULL;
			}
			throw e;
		}
		if (bloque_actual != NULL) { 
			delete (bloque_actual); 
			bloque_actual = NULL;
		}
		cont++;
	}

}

bool ArchivoDatos::cumple_condiciones(TipoStruct &reg,std::vector<ParesDatos*>&consultas){
		unsigned int posicion = 0;
		unsigned int num_campo;
		TipoDato * valor;
		bool ok = true;
		//Mientras se vayan cumpliendo las condiciones y haya consultas
		while (ok && (posicion<consultas.size()) ){
			//Obtengo el numero de campo
			num_campo = consultas[posicion]->get_numero_campo();
			//Obtengo el valor de la consulta
			valor = consultas[posicion]->get_valor();
			//Si lo que estoy comparando son colecciones
			if(valor->get_codigo_tipo() == COLECCION){
				//Casteo a coleccion
				TipoColec* colec = (TipoColec*) (&reg[num_campo]); 
				ok = false;
				unsigned int i = 0;
				//Busco cada dato en la coleccion, y con q uno esta ya cumple la condicion
				while((!ok)&& (i< valor->get_tamanio())){
					ok = colec->buscar_dato((*((TipoColec*)valor))[i]);
					i++;
				}
			}
			else	//Sino verifico igualdad
				ok = (reg[num_campo] == (*valor) );
			
			posicion++;
		}
		return ok;
}

void ArchivoDatos::seleccionar_validos(std::vector<ParesDatos*> &consultas,std::vector<TipoStruct*> &resultado){
	std::vector<TipoStruct*> temp;
	TipoStruct* reg;
	//recorro todo el vector de resutados y voy obteniendo sus registros
	for (unsigned int cont=0;cont<resultado.size();cont++){	
		reg=resultado[cont];
		if (cumple_condiciones(*reg,consultas)) //el reg cumple con todas las condiciones de la consulta
			temp.push_back(reg);
	}
	resultado.clear(); //limpio el vector de resultado, lo vacio
	concatenar(resultado,temp);//cargo en resultado solo los validos para toda la consulta
}

void ArchivoDatos::set_cant_bloques_header(unsigned int cant){
	//Obtengo el bloque header del archivo
	Bloque* bloque_header = Buffer::get_instance()->get_bloque(*this, 0);
	bloque_header->escribir( (char*)&cant,0, sizeof(unsigned int));
	//Guardo el bloque
	Buffer::get_instance()->guardar_bloque(*this, bloque_header);
	delete (bloque_header);
}
		
unsigned int ArchivoDatos::get_cant_bloques_header(){
	//Obtengo el bloque header del archivo
	Bloque* bloque_header = Buffer::get_instance()->get_bloque(*this, 0);
	//Obtengo la cantidad de bloques
	unsigned int result;
	bloque_header->leer((char*)&result, 0, sizeof(unsigned int));
	delete (bloque_header);
	return result;
}

void ArchivoDatos::concatenar(std::vector<TipoStruct*> &v1, std::vector<TipoStruct*> &v2){
	std::vector<TipoStruct*>::iterator it = v2.begin();
	while (it != v2.end()) {
		TipoStruct* reg = (*it);	
		v1.push_back(reg);
		it++;
	}
	v2.clear();
}

unsigned int ArchivoDatos::get_cant_regs_bloque(Bloque* bloque){
	std::vector<TipoStruct *> elementos_bucket;
	TipoStruct * reg_aux = (TipoStruct*)this->get_registro_base()->clonar();
	bloque->get_registros_desde (reg_aux, elementos_bucket,get_primera_pos());
	delete reg_aux;
	unsigned int retorno = elementos_bucket.size();
	
	std::vector<TipoStruct *>::iterator it = elementos_bucket.begin();
	while(it != elementos_bucket.end() ){
		delete (*it);
		it++;
	}
	return (retorno);
}
	
unsigned int ArchivoDatos::get_cant_regs() throw(ExcepArchCerrado){
	unsigned int cont = 0;
	Bloque* bloque_actual;
	
	for(unsigned int i = 1; i < this->cant_bloques; i++ ){
		//obtengo el bloque nro i desde el buffer
		bloque_actual = Buffer::get_instance()->get_bloque(*this,i);
		//le pido al bloque la cant de registros contenidos en el mismo
		cont += get_cant_regs_bloque(bloque_actual);
		//Borro el bloque que me devolvio el buffer
		delete (bloque_actual);
	}
	return cont;
}
